#include "Win32Window.h"


namespace my
{
	static LPCTSTR s_WindowClassName = TEXT("My Win32 Window");


	WindowEventHandlers *GetHandlers(HWND hWnd)
	{
		return reinterpret_cast<WindowEventHandlers *>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
	}


	LRESULT CALLBACK MsgHandler_1(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) noexcept
	{
		switch (msg)
		{
		case WM_CREATE:
		{
			auto &func = GetHandlers(hWnd)->OnCreate;
			if (func)
			{
				func();
				return 0;
			}
			break;
		}
		case WM_DESTROY:
		{
			auto &func = GetHandlers(hWnd)->OnDestroy;
			if (func)
			{
				func();
				return 0;
			}
			break;
		}
		case WM_SIZE:
		{
			auto &func = GetHandlers(hWnd)->OnResized;
			if (func)
			{
				func(LOWORD(lParam), HIWORD(lParam));
				return 0;
			}
			break;
		}
		case WM_MOVE:
		{
			auto &func = GetHandlers(hWnd)->OnMoved;
			if (func)
			{
				func(LOWORD(lParam), HIWORD(lParam));
				return 0;
			}
			break;
		}
		// 	case WM_PAINT:
		// 	{
		// 		HDC hDC = GetDC(hWnd);
		// 		ReleaseDC(hWnd, hDC);
		// 		return 0;
		// 	}
		}

		return DefWindowProc(hWnd, msg, wParam, lParam);
	}


	LRESULT CALLBACK MsgHandler_0(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) noexcept
	{
		// use create parameter passed in from CreateWindow() to store window class pointer at WinAPI side
		if (msg == WM_NCCREATE)
		{
			const CREATESTRUCTW *const pCreate = reinterpret_cast<CREATESTRUCTW *>(lParam);
			SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pCreate->lpCreateParams));
			SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(MsgHandler_1));
			return MsgHandler_1(hWnd, msg, wParam, lParam);
		}
		// if we get a message before the WM_NCCREATE message, handle with default handler
		return DefWindowProc(hWnd, msg, wParam, lParam);
	}


	class AutoRegisterClass
	{
	private:
		AutoRegisterClass()
		{
			m_hInst = GetModuleHandle(nullptr);

			WNDCLASSEX wc = { 0 };
			wc.cbSize = sizeof(wc);
			wc.style = CS_OWNDC;
			wc.lpfnWndProc = MsgHandler_0;
			wc.cbClsExtra = 0;
			wc.cbWndExtra = 0;
			wc.hInstance = m_hInst;
			wc.hIcon = nullptr;
			wc.hCursor = nullptr;
			wc.hbrBackground = nullptr;
			wc.lpszMenuName = nullptr;
			wc.lpszClassName = s_WindowClassName;
			wc.hIconSm = nullptr;

			RegisterClassEx(&wc);
		}

		~AutoRegisterClass()
		{
			UnregisterClass(s_WindowClassName, m_hInst);
		}

		HINSTANCE m_hInst;

	public:
		static AutoRegisterClass &Ensure()
		{
			static AutoRegisterClass s_instance;
			return s_instance;
		}

		HINSTANCE GetHInst() const
		{
			return m_hInst;
		}
	};


	Win32Window::Win32Window()
	{
	}


	Win32Window::~Win32Window()
	{
		Destroy();
	}


	void Win32Window::Create(int width, int height)
	{
		Destroy();

		HINSTANCE hInst = AutoRegisterClass::Ensure().GetHInst();

		DWORD dwStyle = WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU;

		RECT wr;
		wr.left = 100;
		wr.right = width + wr.left;
		wr.top = 100;
		wr.bottom = height + wr.top;
		if (AdjustWindowRect(&wr, dwStyle, FALSE) == 0)
		{
			wr.left = 0;
			wr.top = 0;
			wr.right = width;
			wr.bottom = height;
		}

		m_hWnd = CreateWindow(s_WindowClassName, TEXT(""), dwStyle,
			CW_USEDEFAULT, CW_USEDEFAULT, wr.right - wr.left, wr.bottom - wr.top,
			nullptr, nullptr, hInst, &get_event_handlers());

		ShowWindow(m_hWnd, SW_RESTORE);
	}


	void Win32Window::Destroy()
	{
		if (m_hWnd)
		{
			DestroyWindow(m_hWnd);
			m_hWnd = nullptr;
		}
	}
}
